package com.kly.controller;

import cn.dev33.satoken.annotation.SaCheckLogin;
import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.ShearCaptcha;
import cn.hutool.captcha.generator.MathGenerator;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.script.ScriptUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.kly.common.BaseResponse;
import com.kly.common.DeleteRequest;
import com.kly.common.ResultUtils;
import com.kly.constants.UserConstant;
import com.kly.domain.dto.user.*;
import com.kly.domain.entity.User;
import com.kly.domain.entity.UserFollow;
import com.kly.domain.vo.*;
import com.kly.exception.BusinessException;
import com.kly.exception.ErrorCode;
import com.kly.exception.ThrowUtils;
import com.kly.manager.CosManager;
import com.kly.manager.auth.StpKit;
import com.kly.manager.auth.anno.SaSpaceCheckRole;
import com.kly.service.UserFollowService;
import com.kly.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.connection.BitFieldSubCommands;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

import static com.kly.constants.RedisConstant.*;
import static com.kly.constants.UserConstant.DEFAULT_PASSWORD;

/**
 * 用户控制器
 *
 * @author admin
 * @date 2025/3/2
 */
@RestController
@RequestMapping("/user")
@Slf4j
public class UserController {

    @Resource
    private UserService userService;

    @Resource
    private UserFollowService userFollowService;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private CosManager cosManager;

    /**
     * 用户注册
     *
     * @param userRegisterRequest
     * @return
     */
    @PostMapping("/register")
    public BaseResponse<Long> userRegister(@RequestBody UserRegisterRequest userRegisterRequest) {
        ThrowUtils.throwIf(userRegisterRequest == null, ErrorCode.PARAMS_ERROR);
        String userAccount = userRegisterRequest.getUserAccount();
        String userPassword = userRegisterRequest.getUserPassword();
        String checkPassword = userRegisterRequest.getCheckPassword();
        Long id = userService.userRegister(userAccount, userPassword, checkPassword);
        return ResultUtils.success(id);
    }

    /**
     * 用户密码登录
     */
    @PostMapping("/login")
    // @SaCheckDisable(type = StpKit.SPACE_TYPE)
    public BaseResponse<LoginUserVo> userLogin(@RequestBody UserLoginRequest userLoginRequest, HttpServletRequest request) {
        ThrowUtils.throwIf(userLoginRequest == null, ErrorCode.PARAMS_ERROR);
        String captchaCode = userLoginRequest.getCaptchaCode();
        // 校验图形验证码 TODO knife4j
        verifyCaptchaCode(userLoginRequest.getUserAccount(), captchaCode);
        String userAccount = userLoginRequest.getUserAccount();
        String userPassword = userLoginRequest.getUserPassword();
        userService.userLogin(userAccount, userPassword, request);
        return ResultUtils.success();
    }

    private void verifyCaptchaCode(String account, String captchaCode) {
        ThrowUtils.throwIf(StrUtil.hasBlank(captchaCode), ErrorCode.PARAMS_ERROR);
        String key = LOGIN_CODE_KEY + account;
        String captchaCache = stringRedisTemplate.opsForValue().get(key);
        ThrowUtils.throwIf(!StrUtil.equals(captchaCode, captchaCache), ErrorCode.PARAMS_ERROR, "验证码错误, 请注意区分大小写");
    }

    /**
     * 短信验证码登录
     */
    @PostMapping("/login/sms")
    public BaseResponse<String> userLoginBySms(@RequestBody UserLoginBySmsRequest userLoginBySmsRequest) {
        ThrowUtils.throwIf(userLoginBySmsRequest == null, ErrorCode.PARAMS_ERROR);
        String token = userService.userLoginBySms(userLoginBySmsRequest);
        return ResultUtils.success(token);
    }

    /**
     * 发送短信
     */
    @PostMapping("/send/code")
    public BaseResponse<String> sendCode(@RequestBody UserLoginBySmsRequest userLoginBySmsRequest) {
        ThrowUtils.throwIf(userLoginBySmsRequest == null, ErrorCode.PARAMS_ERROR);
        String phone = userLoginBySmsRequest.getPhone();
        // 校验图形验证码
        verifyCaptchaCode(userLoginBySmsRequest.getEmail(), userLoginBySmsRequest.getCaptchaCode());
        userService.sendCode(phone);
        return ResultUtils.success();
    }

    /**
     * 获取当前登录用户信息
     */
    @GetMapping("/get/login")
    public BaseResponse<LoginUserVo> getCurrentUser(HttpServletRequest request) {
        User user = userService.getLoginUser(request);
        return ResultUtils.success(userService.getLoginUserVo(user));
    }


    /**
     * 用户注销
     */
    @PostMapping("/logout")
    public BaseResponse<Boolean> userLogout(HttpServletRequest request) {
        boolean res = userService.userLogout(request);
        StpKit.DEFAULT.logout();
        StpKit.SPACE.logout();
        return ResultUtils.success(res);
    }


    /**
     * 添加用户 管理员
     */
    @PostMapping("/add")
    @SaSpaceCheckRole(UserConstant.ADMIN_ROLE)
    public BaseResponse<Long> addUser(@RequestBody UserAddRequest userAddRequest) {
        ThrowUtils.throwIf(userAddRequest == null, ErrorCode.PARAMS_ERROR);
        User user = BeanUtil.copyProperties(userAddRequest, User.class);
        // 添加加密密码
        String encryptedPassword = userService.getEncryptedPassword(DEFAULT_PASSWORD);
        user.setUserPassword(encryptedPassword);
        boolean save = userService.save(user);
        ThrowUtils.throwIf(!save, ErrorCode.OPERATION_ERROR);
        return ResultUtils.success(user.getId());
    }

    /**
     * 根据id获取用户 管理员
     */
    @GetMapping("/get")
    @SaSpaceCheckRole(UserConstant.ADMIN_ROLE)
    public BaseResponse<User> getUserById(long id) {
        ThrowUtils.throwIf(id <= 0, ErrorCode.PARAMS_ERROR);
        User user = userService.getById(id);
        user.setUserPassword(null);
        ThrowUtils.throwIf(user == null, ErrorCode.NOT_FOUND_ERROR);
        return ResultUtils.success(user);
    }


    /**
     * 根据id获取用户vo
     */
    @GetMapping("/get/vo")
    public BaseResponse<UserVO> getUserVoById(long id) {
        ThrowUtils.throwIf(id <= 0, ErrorCode.PARAMS_ERROR);
        BaseResponse<User> response = this.getUserById(id);
        User user = response.getData();
        ThrowUtils.throwIf(user == null, ErrorCode.NOT_FOUND_ERROR);
        return ResultUtils.success(userService.getUserVo(user));
    }

    /**
     * 删除用户 管理员
     */
    @PostMapping("/delete")
    @SaSpaceCheckRole(UserConstant.ADMIN_ROLE)
    public BaseResponse<Boolean> deleteUser(@RequestBody DeleteRequest deleteRequest) {
        ThrowUtils.throwIf(deleteRequest == null || deleteRequest.getId() <= 0, ErrorCode.PARAMS_ERROR);
        boolean res = userService.removeById(deleteRequest.getId());
        return ResultUtils.success(res);
    }

    /**
     * 修改用户 管理员
     */
    @PostMapping("/update")
    @SaSpaceCheckRole(UserConstant.ADMIN_ROLE)
    public BaseResponse<Boolean> updateUser(@RequestBody UserUpdateRequest userUpdateRequest) {
        ThrowUtils.throwIf(userUpdateRequest == null || userUpdateRequest.getId() <= 0, ErrorCode.PARAMS_ERROR);
        User user = BeanUtil.copyProperties(userUpdateRequest, User.class);
        boolean res = userService.updateById(user);
        return ResultUtils.success(res);
    }


    /**
     * 获取用户列表 分页
     */
    @GetMapping("/list/page/vo")
    public BaseResponse<PageResult<UserVO>> listUserByPage(UserQueryRequest userQueryRequest) {
        QueryWrapper<User> queryWrapper = userService.getQueryWrapper(userQueryRequest);
        Page<User> page = userService.page(new Page<>(userQueryRequest.getCurrent(), userQueryRequest.getPageSize()), queryWrapper);
        List<UserVO> userVoList = userService.getUserVoList(page.getRecords());

        return ResultUtils.success(
                PageResult.<UserVO>builder()
                        .records(userVoList)
                        .total(page.getTotal())
                        .build()
        );
    }


    /**
     * 用户关注
     */
    @PostMapping("/follow")
    public BaseResponse<Boolean> followUser(@RequestBody UserFollowRequest userFollowRequest, HttpServletRequest request) {
        ThrowUtils.throwIf(userFollowRequest == null, ErrorCode.PARAMS_ERROR);
        User loginUser = userService.getLoginUser(request);
        boolean res = userService.followUser(userFollowRequest, loginUser);
        return ResultUtils.success(res);
    }

    /**
     * 查询是否关注
     */
    @GetMapping("/is/follow")
    public BaseResponse<Boolean> isFollowUser(@RequestParam Long followedUserId, HttpServletRequest request) {
        ThrowUtils.throwIf(followedUserId == null, ErrorCode.PARAMS_ERROR);
        User loginUser = userService.getLoginUser(request);
        boolean exists = userFollowService.lambdaQuery()
                .eq(UserFollow::getFollowingUserId, loginUser.getId())
                .eq(UserFollow::getFollowedUserId, followedUserId)
                .exists();
        return ResultUtils.success(exists);
    }

    /**
     * 查询共同关注
     */
    @GetMapping("/common/follow")
    public BaseResponse<List<UserVO>> listCommonFollowUsers(@RequestParam Long followedUserId, HttpServletRequest request) {
        ThrowUtils.throwIf(followedUserId == null, ErrorCode.PARAMS_ERROR);
        User loginUser = userService.getLoginUser(request);
        ThrowUtils.throwIf(loginUser == null, ErrorCode.NOT_LOGIN_ERROR);
        List<User> commonFollowUser = userService.commonFollowUser(followedUserId, loginUser.getId());
        return ResultUtils.success(userService.getUserVoList(commonFollowUser));
    }

    /**
     * 查询关注列表信息
     */
    @GetMapping("/follow/list")
    public BaseResponse<ScrollResult<PictureVO>> queryFollowInfoList(@RequestParam(value = "max", required = false) Long max, @RequestParam(value = "offset", required = false, defaultValue = "0") Integer offset, @RequestParam(value = "userId") Long userId) {
        max = Optional.ofNullable(max).orElse(System.currentTimeMillis());
        return ResultUtils.success(userService.queryFollowList(max, offset, userId));
    }


    /**
     * 用户签到
     */
    @PostMapping("/sign")
    @SaCheckLogin(type = StpKit.SPACE_TYPE)
    public BaseResponse<Boolean> sign() {
        //     1. 获取当前登录用户
        Long userId = StpKit.SPACE.getLoginIdAsLong();
        //     2. 拼接key sign + userId + 当前日期
        String date = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMM"));
        String key = String.format("%s:%s:%s", USER_SIGN_KEY, userId, date);
        //     3. 用户签到，写入redis
        // 查询是否已签到
        boolean isSign = Boolean.TRUE.equals(stringRedisTemplate.opsForValue().getBit(key, LocalDate.now().getDayOfMonth() - 1));
        ThrowUtils.throwIf(isSign, ErrorCode.OPERATION_ERROR, "已签到");
        stringRedisTemplate.opsForValue().setBit(key, LocalDate.now().getDayOfMonth() - 1, true);
        return ResultUtils.success(true);
    }

    /**
     * 查询今天是否签到
     */
    @GetMapping("/is/sign")
    @SaCheckLogin(type = StpKit.SPACE_TYPE)
    public BaseResponse<Boolean> isSign() {
        //     1. 获取当前登录用户

        Long userId = StpKit.SPACE.getLoginIdAsLong();
        //     2. 拼接key sign + userId + 当前日期
        String date = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMM"));
        String key = String.format("%s:%s:%s", USER_SIGN_KEY, userId, date);
        //     3. 用户签到，写入redis
        // 查询是否已签到
        boolean isSign = Boolean.TRUE.equals(stringRedisTemplate.opsForValue().getBit(key, LocalDate.now().getDayOfMonth() - 1));
        return ResultUtils.success(isSign);
    }


    /**
     * 统计用户连续签到次数
     */
    @GetMapping("/sign/count")
    @SaCheckLogin(type = StpKit.SPACE_TYPE)
    public BaseResponse<Integer> countSign() {
        // 1. 获取当前登录用户
        Long userId = StpKit.SPACE.getLoginIdAsLong();
        // 2. 拼接key sign + userId + 当前日期
        String date = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMM"));
        String key = String.format("%s:%s:%s", USER_SIGN_KEY, userId, date);
        // 3. 统计连续签到次数 bitFiled
        // 获取签到日期列表、
        List<Long> result = stringRedisTemplate.opsForValue().bitField(
                key,
                BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType.unsigned(LocalDate.now().getDayOfMonth())).valueAt(0)
        );
        if (CollUtil.isEmpty(result)) {
            return ResultUtils.success(0);
        }
        Long num = result.get(0);
        if (num == null) {
            return ResultUtils.success(0);
        }
        // 3.1 循环遍历 >>>
        int count = 0;
        while ((num & 1) != 0) {
            count++;
            num = num >>> 1;
        }
        return ResultUtils.success(count);
    }


    /**
     * 发送邮箱验证码
     */
    @GetMapping("/send/code/email")
    public BaseResponse<String> sendCodeByEmail(UserLoginRequest userLoginRequest) {
        String email = userLoginRequest.getEmail();
        ThrowUtils.throwIf(StrUtil.isBlank(email), ErrorCode.PARAMS_ERROR);
        // 校验验证码
        verifyCaptchaCode(userLoginRequest.getEmail(), userLoginRequest.getCaptchaCode());
        userService.sendCodeByEmail(email);
        return ResultUtils.success("ok");
    }

    /**
     * 邮箱登录
     */
    @PostMapping("/login/email")
    public BaseResponse<LoginUserVo> userLoginByEmail(@RequestBody UserLoginBySmsRequest userLoginBySmsRequest, HttpServletRequest request) {
        ThrowUtils.throwIf(userLoginBySmsRequest == null, ErrorCode.PARAMS_ERROR);
        return ResultUtils.success(userService.userLoginByEmail(userLoginBySmsRequest, request));
    }


    /**
     * 获取图形验证码
     */
    @GetMapping("/captcha")
    public BaseResponse<String> getCaptcha(UserLoginRequest userLoginRequest) {
        ThrowUtils.throwIf(userLoginRequest == null, ErrorCode.PARAMS_ERROR);
        String userAccount = userLoginRequest.getUserAccount();
        ThrowUtils.throwIf(StrUtil.isBlank(userAccount), ErrorCode.PARAMS_ERROR, "用户名或邮箱为空");
        String limitKey = LOGIN_CODE_LIMIT_KEY + userAccount;
        String loginKey = LOGIN_CODE_KEY + userAccount;
        if(Boolean.TRUE.equals(stringRedisTemplate.hasKey(limitKey))) {
            return ResultUtils.error(ErrorCode.OPERATION_ERROR, "操作过于频繁, 请稍后重试");
        }
        // // 验证码生成
        // LineCaptcha lineCaptcha = CaptchaUtil.createLineCaptcha(200, 100);
        // String code = lineCaptcha.getCode();
        ShearCaptcha captcha = CaptchaUtil.createShearCaptcha(200, 45, 4, 4);
        // 自定义验证码内容为四则运算方式
        captcha.setGenerator(new MathGenerator());
        // 重新生成code
        captcha.createCode();
        String code = captcha.getCode();
        // 移除=
        code = code.replace("=", "");
        code = String.valueOf(ScriptUtil.eval(code));
        // 保存到redis中
        // 用户userAccount
        stringRedisTemplate.opsForValue().set(loginKey, code, ONE_TIME_TTL, TimeUnit.MINUTES);
        stringRedisTemplate.opsForValue().setIfAbsent(limitKey, "1", 1, TimeUnit.MINUTES);

        // 上传到cos中
        //     获取文件名
        String key = "captcha";
        File tempFile = null;
        try {
            //     创建临时文件
            tempFile = File.createTempFile("my-temp", "");
            captcha.write(tempFile);
            //     上传文件到 COS
            String url = cosManager.uploadFile(key, UUID.randomUUID().toString(), tempFile);
            return ResultUtils.success(url);
        } catch (Exception e) {
            log.error("上传文件失败", e);
        } finally {
            if (tempFile != null) {
                tempFile.delete();
            }
        }
        throw new BusinessException(ErrorCode.OPERATION_ERROR, "获取验证码失败");
    }


}
